package datagram;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;

import javax.imageio.ImageIO;

public class Scalegram extends Datagram {
	private double[][]		value = null;
	private int 			mapArea, mapCount;
	private int 			resolution, roiThreshold;
	
	private int 			roiWidth, roiHeight;
	private int 			roiMinX, roiMinY, roiMaxX, roiMaxY;
	private int 			scaleWidth, scaleHeight;
	private int 			mapWidth, mapHeight;
	
	public Scalegram( BufferedImage image ) {
		super(image);
	}
	
	public Scalegram( BufferedImage image, int resolution, int roiThreshold ) {
		this(image);
		this.resolution = resolution;
		this.roiThreshold = roiThreshold;
		
		calculate();
	}

	public boolean prepare() {
		if (!isLoaded()) {
			System.out.println("ERROR: cannot prepare non-loaded scalegram");
			return false;
		}
		
		int color;
		
		// Calculate ROI data
		roiMaxX = 0;
		roiMaxY = 0;
		roiMinX = image.getWidth();
		roiMinY = image.getHeight();
		
		for (int y = 0; y < image.getHeight(); y++) {
			for (int x = 0; x < image.getWidth(); x++) {
				color = image.getRGB(x,y) & 0x000000FF;
				if (color < roiThreshold) {
					if (x < roiMinX) roiMinX = x;
					if (y < roiMinY) roiMinY = y;
					if (x > roiMaxX) roiMaxX = x;
					if (y > roiMaxY) roiMaxY = y;
				}
			}
		}
		
		roiWidth  = roiMaxX - roiMinX;
		roiHeight = roiMaxY - roiMinY;
		
		mapWidth  = (int) Math.ceil( roiWidth * 1.0/resolution );
		mapHeight = (int) Math.ceil( roiHeight * 1.0/resolution );
		
		scaleWidth  = (int) (roiWidth / mapWidth);
		scaleHeight = (int) (roiHeight / mapHeight);
		
		mapCount = scaleWidth * scaleHeight;
		mapArea = mapWidth * mapHeight;
		
		// Initialize arrays
		
		value = new double[scaleWidth][scaleHeight];
		
		return true;
	}
	
	@Override
	public double calculate() {
		if (!prepare()) {
			System.out.println("ERROR: could not prepare scalegram");
			return 0.0;
		}
		
		int color;
		int minx, miny, maxx, maxy;
		double avg = 0.0;
		
		// Build datagram
		for (int i = 0; i < scaleHeight; i++) {
			for (int j = 0; j < scaleWidth; j++) {
				minx = roiMinX + mapWidth * i;
				miny = roiMinY + mapHeight * j;
				maxx = Math.min(minx + mapWidth, roiMaxX);
				maxy = Math.min(miny + mapHeight, roiMaxY);
				value[j][i] = 0.0;
				
				for (int x = minx; x < maxx; x++) {
					for (int y = miny; y < maxy; y++) {
						color = image.getRGB(x,y) & 0x000000FF;
						value[j][i] += Math.pow(color * 1.0/255, 2);
					}
				}
				
				value[j][i] /= mapArea;
				avg += value[j][i]/mapCount;
			}
		}

		return avg;
	}
	
	@Override
	public double difference( Datagram datagram ) {
		if (!(datagram instanceof Scalegram))
			return 1.0;
		
		if (!isLoaded() || !datagram.isLoaded())
			return 1.0;
		
		Scalegram scalegram = (Scalegram) datagram;
		int width = Math.max(scaleWidth, scalegram.scaleWidth);
		int height = Math.max(scaleHeight, scalegram.scaleHeight);
		double diff = 0;
		
		if (Math.abs(scaleWidth - scalegram.scaleWidth) > 1 ||
			Math.abs(scaleHeight - scalegram.scaleHeight) > 1)
			return 1.0;
		
		for (int i = 0; i < height; i++) {
			for (int j = 0; j < width; j++) {
				if (i < scalegram.scaleHeight && j < scalegram.scaleWidth &&
					i < scaleHeight && j < scaleWidth)
					diff += Math.abs(value[j][i] - scalegram.value[j][i]);
				else
					diff += 1.0;
			}
		}
		
		return diff / mapCount;
	}
}
